#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#define MAXLINE 32
#define error_exit(msg) \
	do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char **argv) {
	int listenfd, connfd;
	socklen_t clilen;
	struct sockaddr_in cliaddr, servaddr; // 网际套接字地址结构
	char buff[MAXLINE];
	time_t ticks;

	// 创建网际(AF_INET)字节流(SOCK_STREAM)套接字(TCP)
	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		error_exit("socket"); 
	
	// 绑定端口到上述套接字
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET; // 地址族
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY指定IP地址为0.0.0.0（所有地址）
	servaddr.sin_port = htons(13); // 端口号（主机字节序到网络字节序）
	if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
		error_exit("bind"); 
			
	// 将上述套接字转换成监听套接字（监听描述符），这样客户的连接就可在该套接字上由内核接受
	if (listen(listenfd, 1024) < 0) // 指定系统内核允许在该监听描述符上排队的最大客户连接数
		error_exit("listen");

	for ( ; ; ) {
		// 睡眠等待客户的连接到达并被内核接受（三路握手）。握手完毕返回已连接描述符
		clilen = sizeof(cliaddr);
		if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) 
			error_exit("accept");					
		printf("connection from %s, port %d\n", 
			inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)), 
			ntohs(cliaddr.sin_port));	
			
		ticks = time(NULL);
		snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
		// 发送应答（将当前时间字符串写给客户）
		//if (write(connfd, buff, strlen(buff)) != strlen(buff))
		//	error_exit("write");
		int n, count = 0;
		while (1) {
			n = write(connfd, buff, strlen(buff));
			if (n == strlen(buff))
				printf("-%d,%d-", count++, n);
			else {
				printf("===%d,%d===\n", strlen(buff), n);
				error_exit("write");					
			}	
		} 

		// 终止连接（引发正常的终止序列：每个方向上发送一个FIN，每个FIN又由各自的对端确认）
		close(connfd); 
	}
	return 0;
}
